<template>
	<view
		:style="{
			display: showCanvas === true ? 'block' : 'none',
		}"
	>
		<view class="poster-container">
			<!-- Canvas 渲染模板，必须要隐藏起来，只是用来生成图片 -->

			<!-- #ifdef MP-ALIPAY -->
			<canvas
				canvas-id="posterCanvas"
				id="posterCanvas"
				type="2d"
				:style="{
					width: canvasWidth * dpr + 'px',
					height: canvasHeight * dpr + 'px',
					background: 'transparent',
				}"
				class="hidden-canvas"
			/>
			<!-- #endif -->

			<!-- #ifndef MP-ALIPAY -->
			<canvas
				canvas-id="posterCanvas"
				id="posterCanvas"
				:style="{
					width: canvasWidth * dpr + 'px',
					height: canvasHeight * dpr + 'px',
					background: 'transparent',
				}"
				class="hidden-canvas"
			/>
			<!-- #endif -->

			<!-- 触发按钮 -->
			<slot name="trigger">
				<button :loading="saving" @click="generate" class="btn-generate">
					{{ generateBtnText }}
				</button>
			</slot>

			<!-- 海报预览 -->
			<image
				v-if="posterImage"
				:src="posterImage"
				mode="aspectFit"
				class="poster-image"
				@click="previewImage"
			/>

			<!-- 操作按钮 -->
			<view v-if="posterImage && isPosterImage" class="actions">
				<button @click="savePoster" class="btn-save">保存到相册</button>
			</view>

			<!-- 加载中弹窗 -->
			<uni-popup v-model="loading" type="center" :mask-click="false">
				<view class="popup-content">
					<text>海报生成中...</text>
				</view>
			</uni-popup>
		</view>
	</view>
</template>

<script>
export default {
	name: 'PosterGenerator',
	// getTempPath 返回图片临时路径
	emits: ['getTempPath'],
	props: {
		// 海报配置
		config: {
			type: Object,
			required: true,
			default() {
				return {};
			},
		},
		// 是否显示 canvas（用于控制渲染时机）
		showCanvas: {
			type: Boolean,
			default: false,
		},
		// 是否显示导出按钮
		isPosterImage: {
			type: Boolean,
			default: false,
		},
		// 生成海报按钮文字
		generateBtnText: { type: String, default: '生成海报' },
	},
	data() {
		return {
			posterImage: '', // 生成的图片路径
			saving: false, // 是否正在保存
			loading: false, // 是否显示加载弹窗
			ctx: null, // Canvas 2D 上下文
			canvasWidth: 375,
			canvasHeight: 600,
			dpr: 1, // 设备像素比
		};
	},
	mounted() {
		const config = this.mergeConfig();
		console.log('海报配置++++=====', config);
		this.initCanvas(config);

		// 获取设备像素比（用于高清渲染）
		try {
			const info = uni.getSystemInfoSync();
			this.dpr = info.pixelRatio || 1;
		} catch (e) {
			this.dpr = 1;
		}
	},
	methods: {
		async generate() {
			const that = this;
			if (that.saving) return;
			that.saving = true;
			that.loading = true;

			try {
				const config = that.mergeConfig();
				console.log('开始生成海报', config);
				// 修复：等待nextTick完成后再绘制（确保Canvas初始化）
				await that.$nextTick();
				await that.drawPoster(config);
			} catch (err) {
				console.error('海报生成失败:', err);
				uni.showToast({
					title: '失败: ' + (err.message || '未知错误'),
					icon: 'error',
					duration: 3000,
				});
				throw err;
			} finally {
				that.saving = false;
				that.loading = false;
			}
		},

		/**
		 * 合并默认配置
		 */
		mergeConfig() {
			return {
				width: 375,
				height: 600,
				background: '#fff',
				borderRadius: 12,
				elements: [],
				quality: 0.92, // 新增质量参数
				...this.config,
			};
		},

		/**
		 * 修复1：支付宝Canvas初始化（缩放+尺寸设置）
		 */
		initCanvas(config) {
			const that = this;
			that.canvasWidth = config.width || 375;
			that.canvasHeight = config.height || 600;

			that.$nextTick(() => {
				// #ifdef MP-ALIPAY
				my.createSelectorQuery()
					.select('#posterCanvas')
					.node()
					.exec(rect => {
						const canvas = rect[0].node;
						that.canvasRect = canvas; // 缓存实例
						that.ctx = canvas.getContext('2d');

						// 修复：设置Canvas实际像素宽高（适配DPR）
						const renderWidth = that.canvasWidth * that.dpr;
						const renderHeight = that.canvasHeight * that.dpr;
						canvas.width = renderWidth;
						canvas.height = renderHeight;

						// 修复：正确设置缩放（原代码赋值错误）
						that.ctx.scale(that.dpr, that.dpr);

						// 初始化背景色
						that.ctx.fillStyle = '#ffffff';
					});
				// #endif

				// #ifndef MP-ALIPAY

				uni
					.createSelectorQuery()
					.in(that)
					.select(`#${'posterCanvas'}`)
					.boundingClientRect(rect => {
						that.ctx = uni.createCanvasContext('posterCanvas', that);
						// 设置高清渲染
						that.ctx.scale(that.dpr, that.dpr);
						// 设置抗锯齿
						that.ctx.setGlobalAlpha(1);
						that.ctx.setFillStyle('#ffffff');
					})
					.exec();

				// #endif
			});
		},

		/**
		 * 修复2：支付宝图片异步加载等待+预加载复用
		 */
		async drawPoster(config) {
			const that = this;
			const { width, height, background, borderRadius, elements, quality } =
				config;
			// 批量绘制优化
			that.ctx.clearRect(0, 0, width, height);

			// 绘制背景
			if (borderRadius > 0) {
				that.drawRoundRect(
					that.ctx,
					0,
					0,
					width,
					height,
					borderRadius,
					background
				);
			} else {
				// #ifdef MP-ALIPAY
				that.ctx.fillStyle = background;
				// #endif
				// #ifndef MP-ALIPAY
				that.ctx.setFillStyle(background);
				// #endif
				that.ctx.fillRect(0, 0, width, height);
			}

			// 预加载所有图片（统一处理，支付宝复用此结果）
			const imageLoaders = elements
				.filter(el => el.type === 'image' && el.src)
				.map(el => that.loadImage(el.src, that.canvasRect).catch(() => null));
			console.log('预加载所有图片', imageLoaders);
			const loadedImages = await Promise.all(imageLoaders);
			let imgIndex = 0;

			// 批量绘制元素（支付宝等待图片加载）
			for (const el of elements) {
				if (el.type === 'text') {
					that.drawTextElement(that.ctx, el);
				} else if (el.type === 'image') {
					const img = loadedImages[imgIndex++];
					if (img) {
						// 修复：支付宝绘制预加载好的图片（避免二次异步）
						await that.drawImageElement(that.ctx, el, img);
					}
				}
			}

			// 绘制完成（支付宝无需draw，直接导出）
			// #ifndef MP-ALIPAY
			await that.finalDraw();
			// #endif
			await that.getFinalImage(quality);
		},

		/**
		 * 绘制文本元素（优化版）
		 */
		drawTextElement(
			ctx,
			{
				x,
				y,
				text,
				fontSize = 14,
				color = '#000',
				textAlign = 'left',
				fontWeight = 'normal',
			}
		) {
			ctx.save();
			// #ifdef MP-ALIPAY
			ctx.font = `${fontWeight} ${fontSize}px sans-serif`; // 修复：font需先设置
			ctx.fillStyle = color;
			ctx.textAlign = textAlign;
			// #endif
			// #ifndef MP-ALIPAY
			ctx.setFontSize(fontSize);
			ctx.setFillStyle(color);
			ctx.setTextAlign(textAlign);
			ctx.font = `${fontWeight} ${fontSize}px sans-serif`;
			// #endif
			ctx.fillText(text, x, y);
			ctx.restore();
		},

		/**
		 * 绘制图片元素（优化版）
		 */
		drawImageElement(ctx, { x, y, width, height, radius }, img) {
			return new Promise(resolve => {
				ctx.save();
				try {
					if (radius > 0) {
						this.drawRoundRect(ctx, x, y, width, height, radius);
						ctx.clip();
					}
					// #ifdef MP-ALIPAY
					// 直接使用预加载好的图片实例（无需再onload）
					ctx.drawImage(img, x, y, width, height);
					resolve(); // 绘制完成通知
					// #endif
					// #ifndef MP-ALIPAY
					ctx.drawImage(img, x, y, width, height);
					resolve();
					// #endif
				} finally {
					ctx.restore();
				}
			});
		},

		/**
		 * 绘制圆角矩形（优化版）
		 */
		drawRoundRect(ctx, x, y, w, h, r, fill) {
			const radius = Math.min(r, Math.min(w, h) / 2);
			ctx.beginPath();
			ctx.moveTo(x + radius, y);
			ctx.lineTo(x + w - radius, y);
			ctx.quadraticCurveTo(x + w, y, x + w, y + radius);
			ctx.lineTo(x + w, y + h - radius);
			ctx.quadraticCurveTo(x + w, y + h, x + w - radius, y + h);
			ctx.lineTo(x + radius, y + h);
			ctx.quadraticCurveTo(x, y + h, x, y + h - radius);
			ctx.lineTo(x, y + radius);
			ctx.quadraticCurveTo(x, y, x + radius, y);
			ctx.closePath();

			if (fill) {
				// 填充
				// #ifdef MP-ALIPAY
				ctx.fillStyle = fill;
				// #endif
				// #ifndef MP-ALIPAY
				ctx.setFillStyle(fill);
				// #endif
				ctx.fill();
			}
		},

		/**
		 * 修复4：支付宝图片预加载（返回Image实例）
		 */
		loadImage(src, canvas) {
			return new Promise((resolve, reject) => {
				const isNetwork = /^https?:\/\//.test(src);

				// 支付宝获取图片信息后，创建Canvas Image实例
				uni.getImageInfo({
					src,
					success: res => {
						// #ifdef MP-ALIPAY
						// 支付宝需用Canvas.createImage()创建实例
						const img = canvas.createImage();
						img.src = res.path; // 使用本地路径（避免跨域）
						img.onload = () => resolve(img); // 加载完成返回实例
						img.onerror = () => reject(new Error('图片加载失败'));
						// #endif
						// #ifndef MP-ALIPAY
						resolve(res.path);
						// #endif
					},
					fail: err => reject(new Error(`图片信息获取失败: ${err.errMsg}`)),
				});
			});
		},

		finalDraw() {
			return new Promise((resolve, reject) => {
				this.ctx.draw(false, () => {
					resolve();
				});
			});
		},

		/**
		 * 修复5：支付宝导出参数（移除不支持的quality）
		 */
		getFinalImage(quality = 0.92) {
			return new Promise((resolve, reject) => {
				// #ifdef MP-ALIPAY
				if (!this.canvasRect) {
					reject(new Error('支付宝Canvas实例获取失败'));
					return;
				}

				my.canvasToTempFilePath({
					canvas: this.canvasRect, // 必须传Canvas实例
					fileType: 'png',
					// 修复：支付宝不支持quality参数，移除
					success: res => {
						if (this.posterImage) {
							try {
								uni.removeSavedFile({ filePath: this.posterImage });
							} catch (e) {
								console.warn('清理旧图片失败:', e);
							}
						}
						// 支付宝返回apFilePath
						this.posterImage = res.apFilePath || res.tempFilePath;
						this.$emit('getTempPath', this.posterImage);
						uni.showToast({
							title: '生成成功',
							icon: 'success',
							duration: 1500,
						});
						resolve();
					},
					fail: err => {
						reject(
							new Error(
								`支付宝导出失败: ${err.errorMessage || JSON.stringify(err)}`
							)
						);
					},
				});
				// #endif

				// #ifndef MP-ALIPAY
				uni.canvasToTempFilePath(
					{
						canvasId: 'posterCanvas',
						quality: quality,
						fileType: 'png',
						width: this.canvasWidth * this.dpr,
						height: this.canvasHeight * this.dpr,
						destWidth: this.canvasWidth * this.dpr,
						destHeight: this.canvasHeight * this.dpr,
						success: res => {
							// 清理旧图片资源
							if (this.posterImage) {
								try {
									uni.removeSavedFile({ filePath: this.posterImage });
								} catch (e) {
									console.warn('清理旧图片失败:', e);
								}
							}
							this.posterImage = res.tempFilePath;
							this.$emit('getTempPath', this.posterImage);
							uni.showToast({
								title: '生成成功',
								icon: 'success',
								duration: 1500,
							});
							resolve();
						},
						fail: err => {
							reject(
								new Error(`导出失败: ${err.errMsg || JSON.stringify(err)}`)
							);
						},
					},
					this // 微信需要传 this
				);
				// #endif
			});
		},

		/**
		 * 组件销毁时清理资源
		 */
		beforeDestroy() {
			// 清理生成的图片文件
			if (this.posterImage) {
				try {
					uni.removeSavedFile({ filePath: this.posterImage });
				} catch (e) {
					console.warn('清理图片资源失败:', e);
				}
			}
			// 释放canvas上下文
			this.ctx = null;
		},

		/**
		 * 预览图片
		 */
		previewImage() {
			uni.previewImage({ urls: [this.posterImage] });
		},

		/**
		 * 保存到相册
		 */
		async savePoster() {
			try {
				await this.checkPermission();
				await uni.saveImageToPhotosAlbum({
					filePath: this.posterImage,
					success: () => uni.showToast({ title: '已保存' }),
				});
			} catch (err) {
				uni.showToast({ title: '保存失败', icon: 'error' });
			}
		},

		/**
		 * 检查相册权限（仅小程序）
		 */
		checkPermission() {
			// #ifdef MP-WEIXIN
			return new Promise((resolve, reject) => {
				uni.getSetting({
					success: res => {
						if (!res.authSetting['scope.writePhotosAlbum']) {
							uni.authorize({
								scope: 'scope.writePhotosAlbum',
								success: () => resolve(),
								fail: () => {
									uni.showToast({ title: '请开启相册权限', icon: 'none' });
									reject();
								},
							});
						} else {
							resolve();
						}
					},
				});
			});
			// #endif
			return Promise.resolve();
		},
	},
};
</script>

<style scoped>
/* 样式保持不变 */
.poster-container {
	display: flex;
	flex-direction: column;
	align-items: center;
	padding: 20rpx;
}

/* 隐藏 canvas，但保留其渲染能力 */
.hidden-canvas {
	position: absolute;
	top: -9999px;
	left: -9999px;
	opacity: 0;
}

.btn-generate,
.btn-save {
	width: 80%;
	margin: 10px 0;
	text-align: center;
	line-height: 50px;
	background-color: #007aff;
	color: #fff;
	border: none;
	border-radius: 24px;
	font-size: 16px;
}

.poster-image {
	width: 90%;
	height: 600px;
	margin: 15px 0;
	border-radius: 12px;
	border: 1px solid #eee;
}

.actions {
	width: 100%;
	display: flex;
	justify-content: center;
}

.popup-content {
	padding: 20px;
	background: #fff;
	border-radius: 12px;
	min-width: 200px;
	text-align: center;
}
</style>
